home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
398
/
398.xpi
/
chrome
/
forecastfox.jar
/
content
/
utilities
/
disk-service.js
next >
Wrap
Text File
|
2010-02-04
|
22KB
|
773 lines
/*------------------------------------------------------------------------------
Copyright (c) 2008 Ensolis, LLC. All Rights Reserved.
----------------------------------------------------------------------------*/
/******************************************************************************
* File I/O Constants
*****************************************************************************/
const MODE_RDONLY = 0x01;
const MODE_WRONLY = 0x02;
const MODE_CREATE = 0x08;
const MODE_APPEND = 0x10;
const MODE_TRUNCATE = 0x20;
const XML_MIMETYPE = "text/xml";
const XML_ENCODING = "UTF-8";
const TEXT_MIMETYPE = "text/plain";
const TEXT_ENCODING = "UTF-8";
/******************************************************************************
* Create a error message to log.
*
* @param Original message.
* @param Prefix to the message.
* @return Message used for logging.
*****************************************************************************/
function createMessage(aMessage, aPrefix)
{
return (new Date()).toUTCString() + ": " + aPrefix + ": " + aMessage + "\r\n";
}
/******************************************************************************
* Reads a file from disk and creates a dom document or text string.
* This implements the nsIRunnable interface so it could
* be dispatched to a thread.
*
* @param File to read.
* @param The mimetype to read.
* @return The content property can be read once the reader has run.
*****************************************************************************/
function FileReader(aFile, aMimetype)
{
this.file = aFile;
this.mimetype = aMimetype;
}
FileReader.prototype = {
_content: null,
_file: null,
_mimetype: null,
get content() { return this._content; },
set content(aVal) { this._content = aVal; },
get file() { return this._file; },
set file(aVal) { this._file = aVal; },
get mimetype() { return this._mimetype; },
set mimetype(aVal) { this._mimetype = aVal; },
///////////////////////////
// nsISupports
QueryInterface: function FileReader_QueryInterface(aIID)
{
if (!aIID.equals(Ci.nsIRunnable) &&
!aIID.equals(Ci.nsISupports))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
///////////////////////////
// nsIRunnable
run: function FileReader_run()
{
//make sure the file exists
if (!this.file.exists()) {
this.content = null;
this.file = null;
this.mimetype = null;
return;
}
//create file input stream
var fiStream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
//initialize stream
fiStream.init(this.file, MODE_RDONLY, PERMS_FILE, false);
//read XML mimetype
if (this.mimetype == XML_MIMETYPE)
this._readXML(fiStream);
//read text mimetype
else
this._readText(fiStream);
//close stream
fiStream.close();
//remove variable references
this.file = null;
this.mimetype = null;
},
/**
* Read a XML file.
*
* @param The file input stream.
*/
_readXML: function FileReader__readXML(aStream)
{
/**
* to support earlier versions we need to use a buffered
* input stream. See bug #287409
*/
var biStream = Cc["@mozilla.org/network/buffered-input-stream;1"].
createInstance(Ci.nsIBufferedInputStream);
biStream.init(aStream, 64 * 1024);
//parse dom document from stream
var domParser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
this.content = domParser.parseFromStream(biStream, XML_ENCODING,
biStream.available(),
XML_MIMETYPE);
//close the buffer input stream
biStream.close();
},
/**
* Read a text file.
*
* @param The file input stream.
*/
_readText: function FileReader__readText(aStream)
{
//get a scriptable stream
var siStream = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
siStream.init(aStream);
//convert to text encoding
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = TEXT_ENCODING;
//read stream into a string
var content = new String();
while (siStream.available() > 0) {
var chunk = siStream.read(siStream.available());
content += converter.ConvertToUnicode(chunk);
}
this.content = content;
//close scritable stream
siStream.close();
}
};
/******************************************************************************
* Writes content to disk. The content can be a string, or a dom document.
* This implements the nsIRunnable interface so it could be dispatched.
* to a thread.
*
* @param File to write.
* @param Content to write. Either a dom document, or a string.
* @param If a backup copy of the file should be created.
* @param The mimetype of the content to write. Used to determine write
* mode and if content is dom or string.
*****************************************************************************/
function FileWriter(aFile, aContent, aBackup, aMimeType)
{
//set variables
this.content = aContent;
this.file = aFile;
this.backup = aBackup;
this.mimetype = aMimeType;
}
FileWriter.prototype = {
_content: null,
_file: null,
_backup: null,
_mimetype: null,
get content() { return this._content; },
set content(aVal) { this._content = aVal; },
get file() { return this._file; },
set file(aVal) { this._file = aVal; },
get backup() { return this._backup; },
set backup(aVal) { this._backup = aVal; },
get mimetype() { return this._mimetype; },
set mimetype(aVal) { this._mimetype = aVal; },
///////////////////////////
// nsISupports
QueryInterface: function FileWriter_QueryInterface(aIID)
{
if (!aIID.equals(Ci.nsIRunnable) &&
!aIID.equals(Ci.nsISupports))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
},
///////////////////////////
// nsIRunnable
run: function FileWriter_run()
{
//make sure the file exists
if (!this.file.exists())
this.file.create(this.file.NORMAL_FILE_TYPE, PERMS_FILE);
//create a file output stream
var foStream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
//write XML mimetype
if (this.mimetype == XML_MIMETYPE)
this._writeXML(foStream);
//write text mimetype
else
this._writeText(foStream);
//close stream
if (foStream instanceof Ci.nsISafeOutputStream)
foStream.finish();
foStream.close();
//create a backup
if (this.backup)
this._createBackup();
//remove variable references
this.content = null;
this.file = null;
this.backup = null;
this.mimetype = null;
},
/**
* Write a xml file.
*
* @param The file output stream.
*/
_writeXML: function FileWriter__writeXML(aStream)
{
//initialize stream
aStream.init(this.file, (MODE_WRONLY | MODE_TRUNCATE), PERMS_FILE, 0);
//serialize to stream
var domSerializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
createInstance(Ci.nsIDOMSerializer);
domSerializer.serializeToStream(this.content, aStream, XML_ENCODING);
},
/**
* Write a text file.
*
* @param The file output stream.
*/
_writeText: function FileWriter__writeText(aStream)
{
//initialize stream
aStream.init(this.file, (MODE_WRONLY | MODE_APPEND), PERMS_FILE, 0);
//get the string converter
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = TEXT_ENCODING;
//write to stream
var chunk = converter.ConvertFromUnicode(this.content);
aStream.write(chunk, chunk.length);
var fin = converter.Finish();
if (fin.length > 0)
aStream.write(fin, fin.length);
},
/**
* Create a backup copy of the file being written to.
*/
_createBackup: function FileWriter__createBackup()
{
//replace file extension with .bak
var name = this.file.leafName;
var ext = name.substring(name.lastIndexOf(".") + 1, name.length);
name = name.replace(ext, "bak");
//get the backup file
var backup = this.file.parent.clone();
backup.append(name);
//remove it if it already exists
try {
if (backup.exists())
removeFile(backup);
//copy the new file to the backup location
this.file.copyTo(backup.parent, backup.leafName);
} catch(e) {}
}
};
/******************************************************************************
* Interfaces used by a service for disk I/O functions. Supplies a set
* of often used disk utilities.
*
* @status FROZEN
* @version 1.0
******************************************************************************/
function DiskService()
{
//setup additional interfaces
this._ifaces.push(Ci.nsIObserver);
this._ifaces.push(Ci.nsISupportsWeakReference);
//setup a new error
this._error = Cc["@ensolis.com/forecastfox/error-item;1"].
createInstance(Ci.ffIErrorItem);
}
DiskService.prototype = {
__proto__: new ServiceBase("DiskService"),
_ioSvc: null,
_domParser: null,
_fileWriters: null,
_writeTimer: null,
_types: null,
///////////////////////////
// nsIObserver
observe: function DiskService_observe(aSubject, aTopic, aData)
{
if (aTopic != "timer-callback" || aSubject != this._writeTimer)
return;
//run the next stored writers
for (var path in this._fileWriters) {
this._fileWriters[path].run();
delete this._fileWriters[path];
break;
}
//control the starting-stopping of the timer
this._controlTimer();
},
////////////////////////////////
// ffIService
/**
* Initialize the component. Called by the manager service.
*/
start: function DiskService_start()
{
//setup writers object
this._fileWriters = {};
//setup types object
this._types = {};
//get io service
this._ioSvc = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
//get the dom parser
this._domParser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
//return success
return true;
},
/**
* Destroy the component. Called by the manager service. This may be
* called prior to start so it needs to be safe.
*/
stop: function DiskService_stop()
{
//stop the writer timer
if (this._writeTimer)
this._writeTimer.cancel();
//clear any pending writes
if (this._fileWriters) {
for (var path in this._fileWriters) {
this._fileWriters[path].run();
delete this._fileWriters[path];
}
}
//clear variables
this._ioSvc = null;
this._domParser = null;
this._fileWriters = null;
this._writeTimer = null;
this._types = null;
},
////////////////////////////////
// ffIDiskService
/**
* Get a file from one of our directory types. Creates a unique
* file name if the type is the temp directory.
*
* @param Name of the file to get.
* @param Directory type to get the file from.
* @return File requested.
*/
get: function DiskService_get(aName, aType)
{
var file = null;
//use the cached type file
if (this._types.hasOwnProperty(aType))
file = this._types[aType];
//get directory based on type
else {
switch (aType) {
case TYPE_PROFILE:
file = getKeyedDirectory("ProfD", ["forecastfox"], true);
break;
case TYPE_CACHE:
file = getKeyedDirectory("ProfD", ["forecastfox", "cache"], true);
break;
case TYPE_ICONS:
file = getKeyedDirectory("ProfD", ["forecastfox", "icons"], true);
break;
case TYPE_TEMP:
file = getKeyedDirectory("TmpD", [], false);
break;
case TYPE_DEFAULTS:
file = getInstallDirectory(["defaults"]);
break;
case TYPE_WEATHERFOX:
file = getKeyedDirectory("ProfD", ["weatherfox"], false);
break;
case TYPE_ERRORS:
file = getKeyedDirectory("ProfD", ["forecastfox", "errors"], true);
break;
}
//cache the type
this._types[aType] = file;
}
//clone the directory and append the file name
file = file.clone();
file.append(aName);
//create unique if temp directory
if (aType == TYPE_TEMP)
file.createUnique(file.NORMAL_FILE_TYPE, PERMS_FILE);
//return the file
return file;
},
/**
* Copy a file. It first removes the destination file
* and then copies to the destination file.
*
* @param File to be copied.
* @param File that will be replaced. It may or may not exist.
*/
copy: function DiskService_copy(aFrom, aTo)
{
//remove the to file if it exists
if (aTo.exists())
removeFile(aTo);
//copy from file
aFrom.copyTo(aTo.parent, aTo.leafName);
},
/**
* Clear a directory of the type passed in. This should be used
* very carefully since will wipe out the directory.
*
* @param Type of directory to clear
* @param Only remove files with cache in the name.
*/
clear: function DiskService_clear(aType, aCache)
{
//get the folder of the requested type
var folder = this.get("", aType);
if (!folder.exists())
return;
//make sure its a directory
if (!folder.isDirectory())
return;
//get list of files
var files = folder.directoryEntries;
while (files.hasMoreElements()) {
var file = files.getNext().QueryInterface(Ci.nsIFile);
//remove the file regardless
if (!aCache)
removeFile(file);
//only remove cache files
else {
var name = file.leafName;
if (name.indexOf("cache") != -1)
removeFile(file);
}
}
},
/**
* Reads a file from disk and converts it to a dom document.
*
* @param File to read.
* @return The dom document.
*/
read: function DiskService_read(aFile)
{
return this._dispatchRead(aFile, XML_MIMETYPE);
},
/**
* Reads a file from disk and returns the string content.
*
* @param File to read.
* @return The string content.
*/
readText: function DiskService_readText(aFile)
{
return this._dispatchRead(aFile, TEXT_MIMETYPE);
},
/**
* Writes a dom document to a file.
*
* @param File to write to.
* @param Document to write.
* @param Create a backup of the file.
* @param Run in blocking mode.
*/
write: function DiskService_write(aFile, aDoc, aBackup, aBlocking)
{
this._dispatchWrite(aFile, aDoc, aBackup, aBlocking, XML_MIMETYPE);
},
/**
* Writes a strings content to a file.
*
* @param File to write to.
* @param String to write.
* @param Create a backup of the file.
* @param Run in blocking mode.
* @param Append to file instead of replace.
*/
writeText: function DiskService_writeText(aFile, aContent, aBackup,
aBlocking, aAppend)
{
//get content of the file if we are appending
var content = "";
if (aAppend)
content = this.readText(aFile);
content += aContent;
//dispatch writer
this._dispatchWrite(aFile, content, aBackup, aBlocking, TEXT_MIMETYPE);
},
/**
* Creates an Empty dom document. Need to pass the local name of the
* root node, doctype url, and namespace url.
*
* @param Local name of the root node.
* @param Doctype url.
* @param Namespace url.
* @return An empty DOM document.
*/
create: function DiskService_create(aRoot, aDTD, aNS)
{
//string representation of xml
var contents = "";
contents += "<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE " + aRoot + " SYSTEM \"" + aDTD + "\">\n" +
"<" + aRoot + " version=\"0.9.10\" xmlns=\"" +
aNS + "\"/>";
//return dom document
return this._domParser.parseFromString(contents, XML_MIMETYPE);
},
/*
* Test for a valid dom document.
*
* @param Dom document to validate.
* @param Local name of the root node.
* @return True if document passed and root node is correct.
*/
validate: function DiskService_validate(aDoc, aRoot)
{
//object passed
if(!aDoc)
return false;
//incorrect root
if (aDoc.documentElement.localName != aRoot)
return false;
return true;
},
/**
* Gets the url of a file passed in. If the file doesn't exist,
* a blank string is returned.
*
* @param File to get the url of.
* @return Spec of the URI.
*/
getFileURL: function DiskService_getFileURL(aFile)
{
//use ioservice to create uri
var URI = this._ioSvc.newFileURI(aFile);
//return the spec
return URI.spec;
},
/**
* Write a message to the error log. Any of the params can be null.
* They are skipped if null.
*
* @param Message to write.
* @param Exception that occurred.
* @param File to copy.
*/
log: function DiskService_log(aMessage, aError, aFile)
{
//get the error log file
var file = this.get("errors.log", TYPE_ERRORS);
//create a variable to hold the strings
var content = "";
//append the new message
if (aMessage)
content += createMessage(aMessage, "Message" );
//append the error
if (aError) {
content += createMessage(aError.toString(), "Exception");
//write call stack
var frame = aError.location;
while (frame) {
content += createMessage(frame.toString(), "Stack");
frame = frame.caller;
}
}
//apend the file
if (aFile) {
//get the error directory
var copyFile = this.get("", TYPE_ERRORS);
//determine file name
var name = aFile.leafName;
if (name.indexOf(".") == -1)
name = name + "-err";
else {
var ind = name.indexOf(".");
var ext = name.substring(ind, name.length);
name = name.substring(0, ind);
name = name + "-err" + ext;
}
//copy file to new name and directory
copyFile.append(name);
copyFile.createUnique(copyFile.NORMAL_FILE_TYPE, PERMS_FILE);
this.copy(aFile, copyFile);
//add message
content += createMessage("Error file (" + copyFile.leafName +
") created for " + aFile.leafName + ".", "File");
}
//write the content to disk
if (content.length > 0)
this.writeText(file, content, false, false, true);
},
////////////////////////////////
// Internal Functions
/**
* Dispatch the file reader. This helper function is used
* because the different functions in the interface are used for different
* mimetypes.
*
* @param File to read from.
* @param Mimetype being read.
*/
_dispatchRead: function DiskService__dispatchRead(aFile, aMimeType)
{
//perform any pending writes before reading
var path = aFile.path;
if (this._fileWriters.hasOwnProperty(path))
return this._fileWriters[path].content;
//create a file reader and dispatch it.
var reader = new FileReader(aFile, aMimeType);
reader.run();
//get the created document and return it
return reader.content;
},
/**
* Dispatch the file writer. This helper function is used
* because the different functions in the interface are used for different
* mimetypes.
*
* @param File to write to.
* @param string to write or document to write.
* @param Create a backup of the file.
* @param Run in blocking mode.
* @param Mimetype being written.
*/
_dispatchWrite: function DiskService__dispatchWrite(aFile, aContent, aBackup,
aBlocking, aMimeType)
{
//if we have a pending write to the file remove it
if (this._fileWriters.hasOwnProperty(aFile.path))
delete this._fileWriters[aFile.path];
//create the file writer
var writer = new FileWriter(aFile, aContent, aBackup, aMimeType);
//run inline if blocking
if (aBlocking)
writer.run();
//add to pending writer list
else
this._fileWriters[aFile.path] = writer;
// control the starting or stopping of the write timer
this._controlTimer();
},
_controlTimer: function DiskService__controlTimer() {
// determine if we have pending writes
var pending = false;
for (var path in this._fileWriters) {
pending = true;
break;
}
// we have pending writes and the timer isn't started
if (pending && !this._writeTimer) {
this._writeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._writeTimer.init(this, 60*1000, Ci.nsITimer.TYPE_REPEATING_SLACK);
// no pending writes and the timer is started
} else if (!pending && this._writeTimer) {
this._writeTimer.cancel();
this._writeTimer = null;
}
}
};